extends Node

signal health_changed(health_value)

@onready var body = $body
@onready var turret = $body/turret
@onready var camera = $body/turret/Camera3D

@onready var main_aim_ray = $body/turret/MainAimRay
@onready var t_1_aim_ray = $body/turret/T1AimRay
@onready var t_2_aim_ray = $body/turret/T2AimRay
@onready var t_3_aim_ray = $body/turret/T3AimRay
@onready var t_4_aim_ray = $body/turret/T4AimRay
@onready var t_5_aim_ray = $body/turret/T5AimRay
@onready var b_1_aim_ray = $body/turret/B1AimRay
@onready var b_2_aim_ray = $body/turret/B2AimRay
@onready var b_3_aim_ray = $body/turret/B3AimRay
@onready var b_4_aim_ray = $body/turret/B4AimRay
@onready var b_5_aim_ray = $body/turret/B5AimRay

@onready var aim_rays: Array[RayCast3D] = [main_aim_ray, t_1_aim_ray, t_2_aim_ray, t_3_aim_ray, t_4_aim_ray, t_5_aim_ray, b_1_aim_ray, b_2_aim_ray, b_3_aim_ray, b_4_aim_ray, b_5_aim_ray]

@onready var cooldown_timer = $body/turret/CooldownTimer
@onready var smoky_shot_audio = $body/turret/SmokyShotAudio

# Wheels
@onready var r_1 = $body/R1
@onready var r_2 = $body/R2
@onready var r_3 = $body/R3
@onready var r_4 = $body/R4
@onready var r_5 = $body/R5
@onready var r_6 = $body/R6
@onready var r_7 = $body/R7
@onready var l_1 = $body/L1
@onready var l_2 = $body/L2
@onready var l_3 = $body/L3
@onready var l_4 = $body/L4
@onready var l_5 = $body/L5
@onready var l_6 = $body/L6
@onready var l_7 = $body/L7

@onready var left: Array[VehicleWheel3D] = [l_1, l_2, l_3, l_4, l_5, l_6, l_7]
@onready var right: Array[VehicleWheel3D] = [r_1, r_2, r_3, r_4, r_5, r_6, r_7]
@onready var wheels: Array[VehicleWheel3D] = left + right

# Constants
const ENGINE_POWER = 150
const BRAKE_POWER = 10
const BRAKE_IDLE_POWER = 2
const SPEED_DEAD_ZONE = 0.2
const TURN_POWER_MULTIPLIER = 5
const MAX_SPEED = 5.0

const DRIVE_VALUES = {
	DriveDirection.P: {
		DriveLabels.GAS_LEFT: 0,
		DriveLabels.GAS_RIGHT: 0,
		DriveLabels.BRAKE_LEFT: BRAKE_IDLE_POWER,
		DriveLabels.BRAKE_RIGHT: BRAKE_IDLE_POWER,
	},
	DriveDirection.F: {
		DriveLabels.GAS_LEFT: 1,
		DriveLabels.GAS_RIGHT: 1,
		DriveLabels.BRAKE_LEFT: 0,
		DriveLabels.BRAKE_RIGHT: 0,
	},
	DriveDirection.B: {
		DriveLabels.GAS_LEFT: -1,
		DriveLabels.GAS_RIGHT: -1,
		DriveLabels.BRAKE_LEFT: 0,
		DriveLabels.BRAKE_RIGHT: 0,
	},
	DriveDirection.L: {
		DriveLabels.GAS_LEFT: -TURN_POWER_MULTIPLIER,
		DriveLabels.GAS_RIGHT: TURN_POWER_MULTIPLIER,
		DriveLabels.BRAKE_LEFT: 0,
		DriveLabels.BRAKE_RIGHT: 0,
	},
	DriveDirection.R: {
		DriveLabels.GAS_LEFT: TURN_POWER_MULTIPLIER,
		DriveLabels.GAS_RIGHT: -TURN_POWER_MULTIPLIER,
		DriveLabels.BRAKE_LEFT: 0,
		DriveLabels.BRAKE_RIGHT: 0,
	},
	DriveDirection.FL: {
		DriveLabels.GAS_LEFT: 0,
		DriveLabels.GAS_RIGHT: TURN_POWER_MULTIPLIER,
		DriveLabels.BRAKE_LEFT: BRAKE_POWER,
		DriveLabels.BRAKE_RIGHT: 0,
	},
	DriveDirection.FR: {
		DriveLabels.GAS_LEFT: TURN_POWER_MULTIPLIER,
		DriveLabels.GAS_RIGHT: 0,
		DriveLabels.BRAKE_LEFT: 0,
		DriveLabels.BRAKE_RIGHT: BRAKE_POWER,
	},
	DriveDirection.BL: {
		DriveLabels.GAS_LEFT: 0,
		DriveLabels.GAS_RIGHT: -TURN_POWER_MULTIPLIER,
		DriveLabels.BRAKE_LEFT: BRAKE_POWER,
		DriveLabels.BRAKE_RIGHT: 0,
	},
	DriveDirection.BR: {
		DriveLabels.GAS_LEFT: -TURN_POWER_MULTIPLIER,
		DriveLabels.GAS_RIGHT: 0,
		DriveLabels.BRAKE_LEFT: 0,
		DriveLabels.BRAKE_RIGHT: BRAKE_POWER,
	},
	DriveDirection.H: {
		DriveLabels.GAS_LEFT: 0,
		DriveLabels.GAS_RIGHT: 0,
		DriveLabels.BRAKE_LEFT: BRAKE_POWER,
		DriveLabels.BRAKE_RIGHT: BRAKE_POWER,
	},
}

var health = 3
var is_ready_to_fire = true
var rotation_speed : float = 1.1
# Enums
enum Direction {FORWARD, BACK, HALT, FALL}
enum Track {LEFT, RIGHT, ALL}
# P - idle(parking, hehe) F - front B - back - L - left R - right, FL, FR, BL, BR - combinations of previos four
enum DriveDirection {P ,F, B, L, R, FL, FR, BL, BR, H}
enum DriveLabels {GAS_LEFT, GAS_RIGHT, BRAKE_LEFT, BRAKE_RIGHT}

func _enter_tree():
	set_multiplayer_authority(str(name).to_int())

func _ready():
	if not is_multiplayer_authority(): return
	Input.mouse_mode = Input.MOUSE_MODE_CAPTURED
	camera.current = true

func _unhandled_input(event):
	if not is_multiplayer_authority(): return
	
	if Input.is_action_just_pressed("fire") and is_ready_to_fire:
		smoky_shot_audio.play()
		is_ready_to_fire = false
		cooldown_timer.start()
		apply_recoil()
		var target_array = []
		var found_enemies = []
		var closest_enemy = null
		
		for aim_ray in aim_rays:
			if aim_ray.is_colliding():
				var target = aim_ray.get_collider()
				var target_coordinates = aim_ray.get_collision_point()
				var distance_to_target = body.position.distance_to(target_coordinates)
				target_array.append([target, distance_to_target, target_coordinates])
				
			for target in target_array:
				if target[0].get_class() == "VehicleBody3D":
					found_enemies.append(target)
		
		if found_enemies:	
			closest_enemy = found_enemies[0]
			for enemy in found_enemies:
				if enemy[1] < closest_enemy[1]:
					closest_enemy = enemy
			
		if closest_enemy:
			closest_enemy[0].get_parent().apply_force_to_body.rpc_id(closest_enemy[0].get_parent().get_multiplayer_authority(), closest_enemy[2])
			closest_enemy[0].get_parent().receive_damage.rpc_id(closest_enemy[0].get_parent().get_multiplayer_authority())

func _physics_process(delta):
	if not is_multiplayer_authority(): return
	
	drive(get_drive_direction())
	
	if Input.is_action_just_pressed("reset"):
		body.position = Vector3.ZERO
		body.rotation = Vector3.ZERO
	
	if Input.is_action_pressed("turret_left"):
		turret.rotate_y(rotation_speed * delta)
	if Input.is_action_pressed("turret_right"):
		turret.rotate_y(-rotation_speed * delta)

func get_movement_direction() -> Direction:
	var angle = rad_to_deg(body.linear_velocity.angle_to(body.basis.z))
	var speed = body.linear_velocity.length()
	if speed < SPEED_DEAD_ZONE:
		return Direction.HALT
	elif angle < 10 :
		return Direction.FORWARD
	elif angle > 170:
		return Direction.BACK
	else:
		return Direction.FALL
	return Direction.HALT

func get_engine_power() -> int:
	if body.linear_velocity.length() <= MAX_SPEED/2:
		return ENGINE_POWER
	elif body.linear_velocity.length() <= MAX_SPEED:
		return ENGINE_POWER/2
	return 0

func gas(track: Track, power: float):
	if track == Track.LEFT:
		for wheel in left:
			wheel.engine_force = power
	if track == Track.RIGHT:
		for wheel in right:
			wheel.engine_force = power
	if track == Track.ALL:
		for wheel in wheels:
			wheel.engine_force = power

func brakes(track: Track, power: float):
	if track == Track.LEFT:
		for wheel in left:
			wheel.brake = power
	if track == Track.RIGHT:
		for wheel in right:
			wheel.brake = power
	if track == Track.ALL:
		for wheel in wheels:
			wheel.brake = power

func get_drive_direction() -> DriveDirection:
	var direction: DriveDirection = DriveDirection.P
	if Input.is_action_pressed("move_forward"):
		match get_movement_direction():
			Direction.BACK:
				direction = DriveDirection.H
			_:
				direction = DriveDirection.F
		direction = DriveDirection.F
	if Input.is_action_pressed("move_backward"):
		match get_movement_direction():
			Direction.FORWARD:
				direction = DriveDirection.H
			_:
				direction = DriveDirection.B
	if Input.is_action_pressed("turn_left"):
		match direction:
			DriveDirection.F:
				direction = DriveDirection.FL
			DriveDirection.B:
				direction = DriveDirection.BL
			DriveDirection.H:
				pass
			_: 
				direction = DriveDirection.L
	if Input.is_action_pressed("turn_right"):
		match direction:
			DriveDirection.F:
				direction = DriveDirection.FR
			DriveDirection.B:
				direction = DriveDirection.BR
			DriveDirection.H:
				pass
			_: 
				direction = DriveDirection.R
	return direction

func drive(direction: DriveDirection):
	var coefficients = DRIVE_VALUES.get(direction)
	gas(Track.LEFT, get_engine_power() * coefficients.get(DriveLabels.GAS_LEFT))
	gas(Track.RIGHT, get_engine_power() * coefficients.get(DriveLabels.GAS_RIGHT))
	brakes(Track.LEFT, coefficients.get(DriveLabels.BRAKE_LEFT))
	brakes(Track.RIGHT, coefficients.get(DriveLabels.BRAKE_RIGHT))

@rpc("any_peer")
func receive_damage():
	health -= 1
	if health <= 0:
		health = 3
		body.position = Vector3.ZERO
	health_changed.emit(health)
	
@rpc("any_peer")
func apply_force_to_body(collision_point):
	var direction = (collision_point - body.global_transform.origin).normalized()
	body.apply_impulse(Vector3(direction.x * -300, direction.y * 300, direction.z * -300))

func apply_recoil():
	var turret_forward = turret.global_transform.basis.z.normalized()
	body.apply_impulse(turret_forward * 200, turret.position)

func _on_cooldown_timer_timeout():
	is_ready_to_fire = true
